#include <rtdef.h>
#include "dev_spi.h"
#include <rtdbg.h>
#include <rtthread.h>
#include "Flash.h"
#include "hc32_ll_clk.h"
#include "cpuHc32f460.h"
#include "hc32_ll_fcg.h"
#include "hc32_ll_pwc.h"
#include "hc32_ll_adc.h"
#include "hc32_ll_gpio.h"
#include <dfs_fs.h>
#include "fal.h"
#include "dev_spi_flash_sfud.h"
struct rt_spi_device * spi_dev;
//-----------------------------------------------
//				本文件使用的变量，常量
//-----------------------------------------------
static uint8_t WriteFlashErrorTimes;
static uint8_t g_byFlashErrTimes;	//flash存储器故障次数

/* SPIdma配置结构体 */
static struct rt_spi_configuration spi_flash_cfg = {
    .mode = RT_SPI_MODE_3 | RT_SPI_MSB | RT_SPI_MASTER ,
    .data_width = 8,
    .max_hz = 32 * 1000 * 1000,  // 32MHz
};


/* SPI1硬件初始化 */
static void hc32_spi1_gpio_init(void)
{
    stc_gpio_init_t gpio_init = {
        .u16PinState        = PIN_STAT_RST,
        .u16PinDir          = PIN_DIR_OUT,
        .u16PinOutputType   = PIN_OUT_TYPE_CMOS,
        .u16PinDrv          = PIN_MID_DRV,     
        .u16Latch           = PIN_LATCH_OFF,        
        .u16PullUp          = PIN_PU_OFF,       
        .u16Invert          = PIN_INVT_OFF,  
        .u16ExtInt          = PIN_EXTINT_OFF,       
        .u16PinAttr         = PIN_ATTR_DIGITAL,      
    };
    LL_PERIPH_WE(LL_PERIPH_GPIO);
    // 配置SCK, MOSI, CS为推挽输出
    GPIO_Init(SPI1_SCK_PIN, &gpio_init);
    GPIO_Init(SPI1_MOSI_PIN, &gpio_init);
    GPIO_Init(SPI1_CS_PORT_PIN, &gpio_init);
    
    // 配置MISO为输入
    gpio_init.u16PinDir = PIN_DIR_IN;
    GPIO_Init(SPI1_MISO_PIN, &gpio_init);
    
    // 设置引脚复用功能
    GPIO_SetFunc(SPI1_SCK_PIN,  Func_Spi1_Sck);  // SPI1_SCK
    GPIO_SetFunc(SPI1_MISO_PIN, Func_Spi1_Miso); // SPI1_MISO
    GPIO_SetFunc(SPI1_MOSI_PIN, Func_Spi1_Mosi); // SPI1_MOSI
    
    // 初始时拉高片选
    GPIO_ResetPins(SPI1_CS_PORT_PIN);
     GPIO_SetPins(SPI1_CS_PORT_PIN);
    LL_PERIPH_WP(LL_PERIPH_GPIO);
}


bool Flash_Read(uint16_t Addr, uint8_t *pBuf, uint32_t DatLen)
{
    struct rt_spi_message msg[2];

    rt_uint8_t type[1] =GD25Q64C_CMD_Four ; // 模式
    rt_uint8_t cmd[5] = {0}; // 命令

    //四字节模式

    //写入命令   
    cmd[0] = GD25Q64C_CMD_READ;
    cmd[1] = Addr >> 24;
    cmd[2] = Addr >> 16;
    cmd[3] = Addr >> 8;
    cmd[4] = Addr;
    
    msg[0].send_buf   = type;
    msg[0].recv_buf   = RT_NULL;
    msg[0].length     = 1;
    msg[0].cs_take    = 1;
    msg[0].cs_release = 1;
    msg[0].next       = RT_NULL;
    rt_spi_transfer_message(spi_dev, msg);
    
    msg[0].send_buf   = cmd;
    msg[0].recv_buf   = RT_NULL;
    msg[0].length     = 5;
    msg[0].cs_take    = 1;
    msg[0].cs_release = 0;
    msg[0].next       = &msg[1];
    
    msg[1].send_buf   = RT_NULL;
    msg[1].recv_buf   = pBuf;
    msg[1].length     = DatLen;
    msg[1].cs_take    = 0;
    msg[1].cs_release = 1;
    msg[1].next       = RT_NULL;
    //传输数据
    rt_spi_transfer_message(spi_dev, msg);
    return true;
}

bool Flash_Write(uint16_t Addr, uint8_t *pBuf, uint32_t DatLen)
{
    struct rt_spi_message msg[5];
    rt_uint8_t type =GD25Q64C_CMD_Four ; // 模式
    rt_uint8_t Enbl =GD25Q64C_CMD_WREN ; // 使能
    rt_uint8_t Dise =GD25Q64C_CMD_WRDI ; //禁止写
    rt_uint8_t cmd[5] = {0}; // 命令

    //写入命令   
    cmd[0] = GD25Q64C_CMD_PP;
    cmd[1] = Addr >> 24;
    cmd[2] = Addr >> 16;
    cmd[3] = Addr >> 8;
    cmd[4] = Addr;
    
    msg[0].send_buf   = &type;
    msg[0].recv_buf   = RT_NULL;
    msg[0].length     = 1;
    msg[0].cs_take    = 1;
    msg[0].cs_release = 1;
    msg[0].next       = &msg[1];
        
    msg[1].send_buf   = &Enbl;
    msg[1].recv_buf   = RT_NULL;
    msg[1].length     = 1;
    msg[1].cs_take    = 1;
    msg[1].cs_release = 1;
    msg[1].next       = &msg[2];
    
    msg[2].send_buf   = cmd;
    msg[2].recv_buf   = RT_NULL;
    msg[2].length     = 5;
    msg[2].cs_take    = 1;
    msg[2].cs_release = 0;
    msg[2].next       = &msg[3];
        
    msg[3].send_buf   = pBuf;
    msg[3].recv_buf   = RT_NULL;
    msg[3].length     = DatLen;
    msg[3].cs_take    = 0;
    msg[3].cs_release = 1;
    msg[3].next       = &msg[4];
    
    msg[4].send_buf   = &Dise;
    msg[4].recv_buf   = RT_NULL;
    msg[4].length     = 1;
    msg[4].cs_take    = 1;
    msg[4].cs_release = 1;
    msg[4].next       = RT_NULL;
    
    //传输数据
    rt_spi_transfer_message(spi_dev, msg);
    return true;
}

//---------------------------------------------------------------
//函数功能: 读取一页数据
//
//参数:
//			No[in]			芯片序号，用于片选选择
//			Addr[in]		读数据的起始地址
//			Len[in]			读出长度
//			pBuf[in/out]	读出数据存储缓冲
//
//返回值:  	TRUE:正确读出   FALSE:读出错误  实际没有判断，返回总是正确 
//
//备注:   
//---------------------------------------------------------------
bool ReadFlashPage( uint16_t No, uint32_t Addr, uint16_t Len, uint8_t *pBuf )
{
    return Flash_Read(Addr, pBuf, Len);
}

//-----------------------------------------------
//函数功能: 查询芯片是否空闲，内部函数
//
//参数: 
//			No[in]			芯片序号，用于片选选择
//                    
//返回值:  	TRUE:芯片空闲   FALSE:芯片处于忙状态
//
//备注:   
//-----------------------------------------------
static uint16_t WaitFlashChipDoOver( uint8_t No )
{
	uint8_t Status;
	uint16_t i;
    
    struct rt_spi_message msg[2];
    rt_uint8_t type =GD25Q64C_CMD_RDSR ; // 模式
    rt_uint8_t Enbl =GD25Q64C_CMD_WREN ; // 使能
    rt_uint8_t Dise = 0 ; //读取地址


    msg[0].send_buf   = &type;
    msg[0].recv_buf   = RT_NULL;
    msg[0].length     = 1;
    msg[0].cs_take    = 1;
    msg[0].cs_release = 0;
    msg[0].next       = &msg[1];
        
    msg[1].send_buf   = &Enbl;
    msg[1].recv_buf   = RT_NULL;
    msg[1].length     = 1;
    msg[1].cs_take    = 0;
    msg[1].cs_release = 1;
    msg[1].next       = RT_NULL;
    
    //传输数据
    rt_spi_transfer_message(spi_dev, msg);
    
    msg[0].send_buf   = &Dise;
    msg[0].recv_buf   = &Status;
    msg[0].length     = 1;
    msg[0].cs_take    = 0;
    msg[0].cs_release = 1;
    msg[0].next       = RT_NULL; 
    rt_spi_transfer_message(spi_dev, msg);
    
    for(i=0; i<350; i++)
	{
        rt_thread_mdelay(1);
        rt_spi_transfer_message(spi_dev, msg);
        if( Status & 0x01 )//忙碌状态
		{
			;
		}
		else//已经空闲
		{
			break;
		}
		
		rt_thread_mdelay(1);
    }
    
    if( i>= 350 )
	{
		return false;
	}
    return true;
    
}
//-----------------------------------------------
//函数功能: 擦除FLASH扇区，模块内部函数
//
//参数: 	
//			No[in]			写入芯片序号
//			Addr[in]		写入数据的起始地址
//			Len[in]			写入数据长度
//			pBuf[in]		存储要写入的数据
//
//返回值:  	TRUE:成功		FALSE：失败
//
//备注:
//-----------------------------------------------
bool EraseExtFlashSector( uint16_t No, uint32_t Addr )
{
    struct rt_spi_message msg[3];
    rt_uint8_t type[1] =GD25Q64C_CMD_Four ; // 模式
    rt_uint8_t Enbl[1] =GD25Q64C_CMD_WREN ; // 使能
    rt_uint8_t Dise[1] = 0 ; //读取地址
    rt_uint8_t cmd[5] = {0}; // 命令

    //写入命令   
    cmd[0] = GD25Q64C_CMD_SE;
    cmd[1] = Addr >> 24;
    cmd[2] = Addr >> 16;
    cmd[3] = Addr >> 8;
    cmd[4] = Addr;
    
    spi_dev = (struct rt_spi_device *)rt_device_find(FLASH_SPI_DEVICE_NAME);
    
    msg[0].send_buf   = type;
    msg[0].recv_buf   = RT_NULL;
    msg[0].length     = 1;
    msg[0].cs_take    = 1;
    msg[0].cs_release = 1;
    msg[0].next       = &msg[1];
        
    msg[1].send_buf   = Enbl;
    msg[1].recv_buf   = RT_NULL;
    msg[1].length     = 1;
    msg[1].cs_take    = 1;
    msg[1].cs_release = 1;
    msg[1].next       = &msg[2];
    
    msg[2].send_buf   = cmd;
    msg[2].recv_buf   = RT_NULL;
    msg[2].length     = 5;
    msg[2].cs_take    = 1;
    msg[2].cs_release = 1;
    msg[2].next       = RT_NULL;
    
    //传输数据
    rt_spi_transfer_message(spi_dev, msg);

	if( WaitFlashChipDoOver( No ) != true )
	{
		return false;
	}
	 rt_thread_mdelay(50);
	return true;
}

//-----------------------------------------------
//函数功能: 写入某个页，不能跨页操作，内部函数
//
//参数:
//			No[in]			芯片序号，用于片选选择
//			Addr[in]		操作地址
//			Len[in]			写入长度（注意不要超过1个扇区）
//			pBuf[in/out]	写入的数据缓冲
//
//返回值:  	TRUE:成功		FALSE:失败
//
//备注:
//-----------------------------------------------
static bool WriteFlashPage( uint8_t No, uint32_t Addr, uint16_t Len, uint8_t *pBuf )
{
	uint8_t CmpData[GD25Q64C_PAGE_SIZE];
	uint32_t i;
	
	
	if(WaitFlashChipDoOver( No ) != true)
	{
		return false;
	}
	
	//如果地址为扇区的起始地址 将该扇区擦除
	if( (Addr % SECTOR_SIZE) == 0 )
	{
		if( EraseExtFlashSector( No, Addr ) != true )//擦除进行2次重试
		{
			EraseExtFlashSector( No, Addr );
		}
	}

	Flash_Write(Addr, pBuf, Len);
	
	//检查是否写完
	if( WaitFlashChipDoOver( No ) != true )
	{
		return false;
	}
	
	//回读比较
	if( ReadExtFlash( No, Addr, Len, CmpData ) == true )
	{
		if( memcmp( pBuf, CmpData, Len ) != 0 )
		{
			return false;
		}
	}
	return true;
}
static void ResetFlash(){};
//-----------------------------------------------
//函数功能: 对Flash芯片进行页写操作，含重试，内部函数
//
//参数: 	
//			No[in]			芯片序号，用于片选选择
//			Addr[in]		操作地址
//			Len[in]			写入长度（注意不要超过1个扇区）
//			pBuf[in/out]	写入的数据缓冲
//
//返回值:  	TRUE:成功		FALSE:失败
//
//备注:
//-----------------------------------------------
static bool WriteFlashPageLogic( uint8_t No, uint32_t Addr, uint16_t Len, uint8_t *pBuf )
{
	uint32_t i;
	
	for(i=0; i<FLASH_MAX_WRITE_COUNT; i++)
	{
		if( WriteFlashPage(No, Addr, Len, pBuf) == true )
		{
			WriteFlashErrorTimes = 0;
			g_byFlashErrTimes = 0;

			return true;
		}
		else
		{
            rt_kprintf("write freeze failed add is %x \r\n",Addr);
		}
		//复位芯片
		ResetFlash();
	}
}

//-----------------------------------------------
//函数功能: 从Flash芯片内读取数据，模块内函数
//
//参数:
//			No[in]			芯片序号，用于片选选择
//			Addr[in]		读数据的起始地址
//			Len[in]			读出长度
//			pBuf[in/out]	读出数据存储缓冲
//
//返回值:  	TRUE:正确读出   FALSE:读出错误  实际没有判断，返回总是正确
//
//备注:
//-----------------------------------------------
bool ReadExtFlash(uint16_t No, uint32_t Addr, uint16_t Len, uint8_t * pBuf)
{
	uint8_t i,j,Times;
	uint8_t ReadBuf[GD25Q64C_PAGE_SIZE];
	uint16_t DoLength;
		
	Times = Len/sizeof(ReadBuf);
	Times += 1;
	
	for(i=0; i<Times; i++)
	{
		if( i == (Times-1) )//最后一次
		{
			DoLength = Len-(uint16_t)sizeof(ReadBuf)*(Times-1);
		}
		else
		{
			DoLength = sizeof(ReadBuf);
		}
		
		for(j=0; j<2; j++)//最多重试2遍
		{
			ReadFlashPage(No, Addr, DoLength, pBuf);
			ReadFlashPage(No, Addr, DoLength, ReadBuf);
			if( memcmp( pBuf, ReadBuf, DoLength ) == 0 )//读两次进行比较，一致则认为读出正确
			{
				break;
			}
		}
		
		Addr += DoLength;
		pBuf += DoLength;
	}
	
	return true;
}

    
//-----------------------------------------------
//函数功能: 写Flash操作，外部函数
//
//参数: 
// 			No[in]			芯片序号，用于片选选择
//			Addr[in]		操作地址
//			Len[in]			写入长度（注意不要超过1个扇区）
//			pBuf[in/out]	写入的数据缓冲                  
//返回值:  TRUE:成功		FALSE:失败
//
//备注:   
//-----------------------------------------------
bool WriteExtFlash( uint8_t No, uint32_t Addr, uint16_t Len, uint8_t *pBuf )
{
	volatile uint32_t TrueAddr;
	short WriteLen;
	uint16_t PageLen;
	
	// 地址取整
	TrueAddr = (Addr / GD25Q64C_PAGE_SIZE) * GD25Q64C_PAGE_SIZE;
	
	// 第一个页中数据长度
	PageLen = GD25Q64C_PAGE_SIZE - (uint16_t)(Addr - TrueAddr);
	
	// 要写入数据的长度
	WriteLen = (short)Len;
	
	// 是否所有数据都在一个扇区内
	if( PageLen > Len )
	{
		PageLen = Len;
		WriteLen -= PageLen;
	}
	
	// 写入数据的起始地址
	TrueAddr = Addr;
	for(;;)
	{
		// 写入一个页
		if( WriteFlashPageLogic(No, TrueAddr, PageLen, pBuf) != true )
		{
			return false;
		}
		
		// 观察是否写入完成
		WriteLen -= PageLen;
		if( WriteLen <= 0 )
		{
			// 已经写完
			break;
		}

		// 操作地址
		TrueAddr += PageLen;
		pBuf += PageLen;

		// 计算长度
		if( WriteLen >= GD25Q64C_PAGE_SIZE )
		{
			PageLen = GD25Q64C_PAGE_SIZE;
		}
		else
		{
			PageLen = WriteLen;
		}
	}
	return true;
}

static void  Create_MTD_AND_MOUNT(const char *parition_name, const char *path)
{
    struct rt_device *mtd_dev = RT_NULL;
    /* 生成 mtd 设备 */
    mtd_dev = (struct rt_device *)fal_mtd_nor_device_create(parition_name);
    if (!mtd_dev)
    {
        LOG_E("Can't create a mtd device on '%s' partition.", parition_name);
    }
    else
    {
        if (dfs_mount(parition_name, path, "lfs", DFS_FS_FLAG_DEFAULT, 0) != 0) 
        {
            /* 格式化文件系统 */
            if (RT_EOK == dfs_mkfs("lfs", parition_name))
            {
                /* 挂载 littlefs */
                if (RT_EOK == dfs_mount(parition_name, path, "lfs", 0, 0))
                {
                    LOG_I("Filesystem initialized!");
                }
                else
                {
                    LOG_E("Failed to initialize filesystem!");
                }
            }
            else
            {
                LOG_E("Failed to Format fs!");
            }
        }
    }
}
int filesystem_mount(void)
{
    if (RT_NULL == rt_sfud_flash_probe(FAL_USING_NOR_FLASH_DEV_NAME, FLASH_SPI_DEVICE_NAME))
    {
        return -RT_ERROR;
    }

    /* 初始化 fal */
    fal_init();
    
    Create_MTD_AND_MOUNT("start","/");
    
    mkdir("/littlefs", 0x777);
    mkdir("/download", 0x777);
    mkdir("/log", 0x777);
    Create_MTD_AND_MOUNT("littlefs","/littlefs");
    Create_MTD_AND_MOUNT("download","/download");
    Create_MTD_AND_MOUNT("log","/log");
    /* 等待Flash设备初始化 */
    rt_device_t dev = rt_device_find(FAL_USING_NOR_FLASH_DEV_NAME);
    if (!dev) {
        rt_kprintf("SPI Flash not found!\n");
        return -RT_ERROR;
    }
    rt_kprintf("LittleFS mounted successfully!\n");
    
}

static int rt_hw_spi_flash_init(void)
{
    // 1. 使能SPI时钟
    FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_SPI1, Enable);
    hc32_spi1_gpio_init();
    // 2. 挂载设备到SPI总线
    LL_PERIPH_WE(LL_PERIPH_GPIO);
    rt_err_t res = rt_hw_spi_device_attach(FLASH_SPI_BUS_NAME, FLASH_SPI_DEVICE_NAME, (void*)SPI1_CS_PPIN);
    LL_PERIPH_WP(LL_PERIPH_GPIO);
        
    if (res != RT_EOK) {
        rt_kprintf("SPI device attach failed!\n");
        return res;
    }
    // 3. 配置SPI设备参数
    ENABLE_FCG0_REG_WRITE();
    spi_dev = (struct rt_spi_device *)rt_device_find(FLASH_SPI_DEVICE_NAME);
    res = rt_spi_configure(spi_dev, &spi_flash_cfg);
    DISABLE_FCG0_REG_WRITE();
    
    if (res != RT_EOK) {
        rt_kprintf("SPI config failed!\n");
        return res;
    }
    return RT_EOK;
}
/* 导出到自动初始化 */
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);



CMD_OPTIONS_STATEMENT(Flash_Operate)
static int Flash_Operate(int argc, char **argv)
{
    uint32_t Reg_Len=0, Read_Len=0;
    uint32_t Rev_addr=0;
    int ReadNo=0;
    int i=0, j=0, k=0;
    rt_uint8_t Revbuf[1124], RevbufString[100], Rev_addrBuf[10],Rev_lenBuf[10] ;
    memset(RevbufString, 0, sizeof(RevbufString));

    if( (argc ==4) && (strcmp(argv[1], "read")== 0))
    {
        if( strlen(argv[2]) > 8 || strlen(argv[3]) > 8 )
        {
            rt_kprintf("addr or size len >8");
            return RT_ERROR;
        }
        
        StringToHexGroupBW((unsigned char *)&Rev_addr,argv[2],strlen(argv[2]));
        StringToHexGroupBW((unsigned char *)&Reg_Len,argv[3],strlen(argv[3]));
        
        do//for(i=0;; i++)
        {
            Read_Len = Reg_Len;
            if( Read_Len >1024)
            {
                Read_Len = ( Reg_Len > i*1024)? 1024 : Reg_Len %1024 ;
            }
            flash_Read_sync(Rev_addr + Read_Len*i, (uint8_t *)&Revbuf, Read_Len);
            

            for( k =0;k<Read_Len; k =k+16)
            {
                HexToAscii(Revbuf+k, RevbufString, 16);
                rt_kprintf("0x%x |", Rev_addr + Read_Len*i+k );
                for( j=0; j<16;j++)
                {
                    rt_kprintf("%-2s ", &RevbufString[j] );
                }
                rt_kprintf("\n");
            }  
            i++;
        }while( i< Reg_Len/1024);
    }
    else if(strcmp(argv[1], "erase") == 0)
    {
        rt_kprintf("禁止擦除 \n"); 
    }
    else if(strcmp(argv[1], "write") == 0)
    {
        
        rt_kprintf("禁止写 \r\n"); 
    }
    else
    {
        goto _usage;
    }

//    HexToAscii(Revbuf, RevbufString, Reg_Len);
//    rt_kprintf("当前 %-12s 为: 0x%-12s\n", EC_EMU_RegData[ReadNo].name, &RevbufString);
    return RT_EOK;
    
_usage:
    rt_kprintf("Usage: flash [options]\n");
    rt_kprintf("[options]:\n");
    rt_kprintf("flash erase \n"); 
    rt_kprintf("flash write \n"); 
    rt_kprintf("flash read  addr(0x) size(0x)\n"); 
    return RT_EOK;
}
CMD_OPTIONS_NODE_START(Flash_Operate)
CMD_OPTIONS_NODE(RT_Object_Class_Thread,       erase,       擦除 )
CMD_OPTIONS_NODE(RT_Object_Class_Thread,       write,       写   )
CMD_OPTIONS_NODE(RT_Object_Class_Thread,       read,        读   )
                                                                        
CMD_OPTIONS_NODE_END                                                    
MSH_CMD_EXPORT_ALIAS(Flash_Operate, flash, flash objects, optenable);